import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:record/record.dart';
import 'package:audioplayers/audioplayers.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mic Test (Windows)',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
      ),
      home: const MicTestPage(),
    );
  }
}

class MicTestPage extends StatefulWidget {
  const MicTestPage({super.key});

  @override
  State<MicTestPage> createState() => _MicTestPageState();
}

class _MicTestPageState extends State<MicTestPage> {
  final _recorder = AudioRecorder();
  final _amplitudes = <double>[]; // recent amplitude values in dBFS
  StreamSubscription<Amplitude>? _ampSub;
  StreamSubscription<Uint8List>? _pcmSub;

  List<InputDevice> _devices = [];
  InputDevice? _selectedDevice;

  bool _hasPermission = false;
  bool _isRecording = false;
  bool _isPaused = false;
  String? _lastSavedPath;

  // PCM stream stats
  int _bytesReceived = 0;
  int _samplesCounted = 0;
  double _rms = 0; // for quick visual

  // === 播放相关 ===
  final AudioPlayer _player = AudioPlayer();
  PlayerState _playerState = PlayerState.stopped;
  String? _nowPlayingPath;
  List<FileSystemEntity> _savedFiles = [];

  @override
  void initState() {
    super.initState();
    _init();
  }

  Future<void> _init() async {
    try {
      _hasPermission = await _recorder.hasPermission();
      if (_hasPermission) {
        _devices = await _recorder.listInputDevices();
        // pick default
        _selectedDevice = _devices.isNotEmpty ? _devices.first : null;
      }
    } catch (e) {
      debugPrint('Init error: $e');
    }
    // 监听播放器状态
    _player.onPlayerStateChanged.listen((s) {
      setState(() => _playerState = s);
    });
    // 初次加载保存目录列表
    await _refreshSavedList();

    if (mounted) setState(() {});
  }

  Future<Directory> _ensureOutputDir() async {
    final dir = await getApplicationDocumentsDirectory();
    final out = Directory('${dir.path}${Platform.pathSeparator}recordings');
    if (!await out.exists()) {
      await out.create(recursive: true);
    }
    return out;
  }

  Future<void> _refreshSavedList() async {
    final out = await _ensureOutputDir();
    final list =
        out
            .listSync()
            .where((e) => e is File && e.path.toLowerCase().endsWith('.wav'))
            .toList()
          ..sort(
            (a, b) => b.statSync().modified.compareTo(a.statSync().modified),
          );
    _savedFiles = list;
    if (mounted) setState(() {});
  }

  RecordConfig _fileWavConfig() {
    return RecordConfig(
      encoder: AudioEncoder.wav,
      sampleRate: 44100,
      numChannels: 1,
      device: _selectedDevice,
      echoCancel: false,
      autoGain: false,
      noiseSuppress: false,
    );
  }

  RecordConfig _pcmStreamConfig() {
    return RecordConfig(
      encoder: AudioEncoder.pcm16bits,
      sampleRate: 44100,
      numChannels: 1,
      device: _selectedDevice,
      streamBufferSize: 2048,
    );
  }

  Future<void> _startFileRecord() async {
    if (!_hasPermission) {
      _hasPermission = await _recorder.hasPermission();
      if (!_hasPermission) {
        if (mounted) {
          ScaffoldMessenger.of(
            context,
          ).showSnackBar(const SnackBar(content: Text('无麦克风权限')));
        }
        return;
      }
    }

    // stop current stream if any
    await _stopStreamInternal();

    final outDir = await _ensureOutputDir();
    final ts = DateTime.now().toIso8601String().replaceAll(':', '-');
    final path = '${outDir.path}${Platform.pathSeparator}rec_$ts.wav';

    try {
      await _recorder.start(_fileWavConfig(), path: path);
      _listenAmplitude();
      _isRecording = true;
      _isPaused = false;
      _lastSavedPath = null; // will be set on stop
      setState(() {});
    } catch (e) {
      debugPrint('Start file record error: $e');
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('启动录音失败: $e')));
      }
    }
  }

  Future<void> _startPcmStream() async {
    if (!_hasPermission) {
      _hasPermission = await _recorder.hasPermission();
      if (!_hasPermission) {
        if (mounted) {
          ScaffoldMessenger.of(
            context,
          ).showSnackBar(const SnackBar(content: Text('无麦克风权限')));
        }
        return;
      }
    }

    // stop file record if any
    if (await _recorder.isRecording()) {
      // switch to stream: cancel file session if it was file-based
      await _recorder.cancel();
    }

    try {
      final stream = await _recorder.startStream(_pcmStreamConfig());
      _listenAmplitude();
      _pcmSub = stream.listen(
        _onPcmChunk,
        onError: (e, st) {
          debugPrint('PCM stream error: $e');
        },
        onDone: () {
          debugPrint('PCM stream done');
        },
      );
      _isRecording = true;
      _isPaused = false;
      _lastSavedPath = null;
      setState(() {});
    } catch (e) {
      debugPrint('Start stream error: $e');
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('启动流式录音失败: $e')));
      }
    }
  }

  void _onPcmChunk(Uint8List bytes) {
    _bytesReceived += bytes.length;
    // convert to int16 for simple RMS calc & preview bar
    final samples = _recorder.convertBytesToInt16(bytes);
    _samplesCounted += samples.length;
    double sumSquares = 0;
    for (final s in samples) {
      final v = s / 32768.0;
      sumSquares += v * v;
    }
    final rms = samples.isNotEmpty
        ? math.sqrt(sumSquares / samples.length)
        : 0.0;
    _rms = rms;
    if (mounted) setState(() {});
  }

  Future<void> _pause() async {
    try {
      await _recorder.pause();
      _isPaused = true;
      setState(() {});
    } catch (e) {
      debugPrint('Pause error: $e');
    }
  }

  Future<void> _resume() async {
    try {
      await _recorder.resume();
      _isPaused = false;
      setState(() {});
    } catch (e) {
      debugPrint('Resume error: $e');
    }
  }

  Future<void> _stop() async {
    try {
      String? path;
      if (await _recorder.isRecording()) {
        path = await _recorder.stop();
      }
      await _stopStreamInternal();
      _isRecording = false;
      _isPaused = false;
      _lastSavedPath = path;
      _cancelAmplitude();
      await _refreshSavedList();
      setState(() {});
    } catch (e) {
      debugPrint('Stop error: $e');
    }
  }

  Future<void> _cancel() async {
    try {
      await _recorder.cancel();
      await _stopStreamInternal();
      _isRecording = false;
      _isPaused = false;
      _lastSavedPath = null;
      _cancelAmplitude();
      setState(() {});
    } catch (e) {
      debugPrint('Cancel error: $e');
    }
  }

  Future<void> _playFile(String path) async {
    try {
      await _player.stop();
      _nowPlayingPath = path;
      await _player.play(DeviceFileSource(path));
    } catch (e) {
      debugPrint('Play error: $e');
      if (mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('播放失败: $e')));
      }
    }
  }

  Future<void> _togglePause() async {
    if (_playerState == PlayerState.playing) {
      await _player.pause();
    } else if (_playerState == PlayerState.paused) {
      await _player.resume();
    }
  }

  Future<void> _stopPlay() async {
    await _player.stop();
    _nowPlayingPath = null;
    setState(() {});
  }

  Future<void> _deleteFile(String path) async {
    try {
      await File(path).delete();
      if (_nowPlayingPath == path) {
        await _stopPlay();
      }
      await _refreshSavedList();
    } catch (e) {
      debugPrint('Delete error: $e');
    }
  }

  Future<void> _stopStreamInternal() async {
    await _pcmSub?.cancel();
    _pcmSub = null;
    _bytesReceived = 0;
    _samplesCounted = 0;
    _rms = 0;
  }

  void _listenAmplitude() {
    _amplitudes.clear();
    _ampSub?.cancel();
    _ampSub = _recorder
        .onAmplitudeChanged(const Duration(milliseconds: 100))
        .listen((amp) {
          // amp.current/ max are dBFS (negative values), clamp to [-60, 0]
          final v = amp.current.clamp(-60.0, 0.0).toDouble();
          _amplitudes.add(v);
          if (_amplitudes.length > 50) {
            _amplitudes.removeAt(0);
          }
          if (mounted) setState(() {});
        });
  }

  void _cancelAmplitude() {
    _ampSub?.cancel();
    _ampSub = null;
    _amplitudes.clear();
  }

  @override
  void dispose() {
    _ampSub?.cancel();
    _pcmSub?.cancel();
    _recorder.dispose();
    _player.dispose();
    super.dispose();
  }

  Widget _buildTopBar() {
    return Padding(
      padding: const EdgeInsets.all(12),
      child: Row(
        children: [
          Expanded(
            child: DropdownButton<InputDevice>(
              value: _selectedDevice,
              isExpanded: true,
              hint: const Text('选择输入设备'),
              items: _devices
                  .map(
                    (d) => DropdownMenuItem(
                      value: d,
                      child: Text('${d.label} (${d.id})'),
                    ),
                  )
                  .toList(),
              onChanged: (d) {
                setState(() => _selectedDevice = d);
              },
            ),
          ),
          const SizedBox(width: 12),
          Icon(
            _hasPermission ? Icons.mic : Icons.mic_off,
            color: _hasPermission ? Colors.green : Colors.red,
          ),
        ],
      ),
    );
  }

  Widget _buildControls() {
    return Wrap(
      spacing: 12,
      runSpacing: 12,
      children: [
        FilledButton.icon(
          onPressed: _isRecording ? null : _startFileRecord,
          icon: const Icon(Icons.fiber_manual_record),
          label: const Text('开始文件录音 (WAV)'),
        ),
        FilledButton.tonalIcon(
          onPressed: _isRecording ? null : _startPcmStream,
          icon: const Icon(Icons.stream),
          label: const Text('开始流式PCM16'),
        ),
        FilledButton.icon(
          onPressed: _isRecording && !_isPaused ? _pause : null,
          icon: const Icon(Icons.pause),
          label: const Text('暂停'),
        ),
        FilledButton.icon(
          onPressed: _isRecording && _isPaused ? _resume : null,
          icon: const Icon(Icons.play_arrow),
          label: const Text('继续'),
        ),
        FilledButton.icon(
          onPressed: _isRecording ? _stop : null,
          icon: const Icon(Icons.stop),
          label: const Text('停止'),
        ),
        OutlinedButton.icon(
          onPressed: _isRecording ? _cancel : null,
          icon: const Icon(Icons.cancel),
          label: const Text('取消'),
        ),
      ],
    );
  }

  Widget _buildAmplitudeStrip() {
    return SizedBox(
      height: 80,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.end,
        children: _amplitudes.map((db) {
          // map -60..0 dB to 0..1
          final t = (db + 60) / 60;
          final tClamped = t.clamp(0.0, 1.0).toDouble();
          final h = 10.0 + 70.0 * tClamped;
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 1),
            child: Container(
              width: 6,
              height: h,
              color: Color.lerp(Colors.red, Colors.green, tClamped),
            ),
          );
        }).toList(),
      ),
    );
  }

  Widget _buildStatus() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('状态: ${_isRecording ? (_isPaused ? '已暂停' : '录音中') : '空闲'}'),
        if (_lastSavedPath != null) ...[
          const SizedBox(height: 4),
          Text('保存: $_lastSavedPath'),
        ],
        const SizedBox(height: 8),
        Text(
          '流数据: ${(_bytesReceived / 1024).toStringAsFixed(1)} KB, 样本: $_samplesCounted',
        ),
        LinearProgressIndicator(
          value: _rms.clamp(0.0, 1.0).toDouble(),
          minHeight: 8,
        ),
      ],
    );
  }

  Widget _buildPlayerPanel() {
    return Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(height: 16),
          const Text('已保存录音'),
          const SizedBox(height: 8),
          Expanded(
            child: _savedFiles.isEmpty
                ? const Center(child: Text('暂无录音'))
                : ListView.separated(
                    itemCount: _savedFiles.length,
                    separatorBuilder: (context, index) =>
                        const Divider(height: 1),
                    itemBuilder: (ctx, idx) {
                      final f = _savedFiles[idx] as File;
                      final stat = f.statSync();
                      final name = f.path.split(Platform.pathSeparator).last;
                      final isPlaying =
                          _nowPlayingPath != null &&
                          _nowPlayingPath == f.path &&
                          _playerState == PlayerState.playing;
                      final isPaused =
                          _nowPlayingPath == f.path &&
                          _playerState == PlayerState.paused;
                      return ListTile(
                        leading: Icon(
                          isPlaying ? Icons.graphic_eq : Icons.audio_file,
                          color: isPlaying ? Colors.green : null,
                        ),
                        title: Text(name),
                        subtitle: Text(
                          '${stat.modified.toLocal()}  •  ${(stat.size / 1024).toStringAsFixed(1)} KB',
                        ),
                        trailing: Wrap(
                          spacing: 8,
                          children: [
                            IconButton(
                              tooltip: isPlaying || isPaused ? '暂停/继续' : '播放',
                              icon: Icon(
                                isPlaying
                                    ? Icons.pause
                                    : isPaused
                                    ? Icons.play_arrow
                                    : Icons.play_arrow,
                              ),
                              onPressed: () async {
                                if (_nowPlayingPath == f.path) {
                                  await _togglePause();
                                } else {
                                  await _playFile(f.path);
                                }
                              },
                            ),
                            IconButton(
                              tooltip: '停止',
                              icon: const Icon(Icons.stop),
                              onPressed: _nowPlayingPath == f.path
                                  ? _stopPlay
                                  : null,
                            ),
                            IconButton(
                              tooltip: '删除',
                              icon: const Icon(Icons.delete_outline),
                              onPressed: () async {
                                final ok = await showDialog<bool>(
                                  context: context,
                                  builder: (c) => AlertDialog(
                                    title: const Text('删除录音'),
                                    content: Text('确定删除 "$name" 吗？'),
                                    actions: [
                                      TextButton(
                                        onPressed: () =>
                                            Navigator.pop(c, false),
                                        child: const Text('取消'),
                                      ),
                                      FilledButton(
                                        onPressed: () => Navigator.pop(c, true),
                                        child: const Text('删除'),
                                      ),
                                    ],
                                  ),
                                );
                                if (ok == true) {
                                  await _deleteFile(f.path);
                                }
                              },
                            ),
                          ],
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mic Test (Windows)'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildTopBar(),
          const Divider(),
          Padding(padding: const EdgeInsets.all(12), child: _buildControls()),
          const Divider(),
          Padding(
            padding: const EdgeInsets.all(12),
            child: _buildAmplitudeStrip(),
          ),
          const Divider(),
          Padding(padding: const EdgeInsets.all(12), child: _buildStatus()),
          const Divider(),
          _buildPlayerPanel(),
        ],
      ),
    );
  }
}
